/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package aptech.eproject.logictics.dao.thinh.ManageTrip;

import aptech.eproject.logictics.db.*;
import java.sql.Timestamp;
import java.util.*;
import javax.ejb.*;
import javax.persistence.*;
import org.jboss.annotation.ejb.cache.simple.CacheConfig;


/**
 *
 * @author HOANGTHINH
 */
@Stateful(name="stateful/ManageTripService")
@CacheConfig(maxSize=5000,idleTimeoutSeconds=300,removalTimeoutSeconds=18000)
@TransactionManagement(value = TransactionManagementType.CONTAINER)
public class ManageTripService  extends AddTripService implements ManageTripRemote {

  

   /**
   * init the route already in trip.
   * get list of route in trip
   * @param tripId - Integer type
   * @return
   *    list route Id each element type integer
   */
  public List listRouteInTrip(int tripId)
  {
      try {
          // Get list route Id of trip
          Query q= em.createQuery("SELECT tr FROM TripRoute tr WHERE tr.trip.tripId= ?1 AND tr.isDeleted='False' AND tr.status != ?2");
          q.setParameter(1,tripId);
          q.setParameter(2,TripRouteStatus.Arrived);
          List routeIds=q.getResultList();
          for (int i = 0; i < routeIds.size(); i++) {
              TripRoute tr = (TripRoute)routeIds.get(i);
              dictRouteOfTrip.put(tr.getRoute().getRouteId()+","+tr.getRouteOrder(), 0);
          }
          return routeIds;
      }
      catch(NullPointerException npe)
      {
          System.err.println("Cannot find information about query !!!");
          return null;
      }
      catch (Exception e) {
          e.printStackTrace();
          return null;
      }
  }

  /**
   * When click cancel to close dialog call this method
   * This method test to see if this trip is have package come to destination branch or not
   * @param tripId
   * @param warehouseId
   * @return
   *    return null: If trip isn't have any package come to destination branch .
   *    return dictInvalidPackage. Call getMessage to get message show on dialog
   */
  public Dictionary<Integer,Integer> cancelPackageTrip(int tripId,int warehouseId)
  {
      Dictionary<Integer,Integer> dictInvalidPackage=new Hashtable<Integer, Integer>();
      List packageInTrips=listPackageInTrip(tripId);
      for (int i = 0; i < packageInTrips.size(); i++) {
          aptech.eproject.logictics.db.Package p = (aptech.eproject.logictics.db.Package) packageInTrips.get(i);
          int packageId=p.getPackageId();
          if(isPackageAtDesBranch(packageId, warehouseId))
          {
              dictInvalidPackage.put(packageId, 2);
          }
      }
      if(dictInvalidPackage.size()==0)
      {
          dictInvalidPackage=null;
      }
      return dictInvalidPackage;
  }
  /**
   * Use to test whether package is come to destination branch
   * @param packageId
   * @param warehouseId
   * @return
   *    true: package come to destination branch
   *    false: package didn't come
   */
  public boolean isPackageAtDesBranch(int packageId,int warehouseId)
  {
      aptech.eproject.logictics.db.Package p=em.find(aptech.eproject.logictics.db.Package.class, packageId);
      if(p.getDelivery().getDestinationBranch().getBranchId()==warehouseId)
      {
          return true;
      }
      else return false;
  }

  /**
   * Return list package at destion branch element packageId
   * @param tripId
   * @return
   *    lstPackageAtDesbranch element package Id
   *    null :If didn't have package come to desbranch
   */
  public Dictionary isListPackageAtDesBranch(TripRoutePK tripRoutePK,int currentBranchId)
  {
      Dictionary lstPackageAtDesbranch=new Hashtable<Integer, Integer>();
      try
      {
          TripRoute tr= em.find(TripRoute.class, tripRoutePK);
        List packages=listPackageInTrip(tr.getTrip().getTripId());
       // System.out.println(packages.size());
          for (int i = 0; i < packages.size(); i++) {
              aptech.eproject.logictics.db.Package p =(aptech.eproject.logictics.db.Package) packages.get(i);
              if(isPackageAtDesBranch(p.getPackageId(),currentBranchId))
              {
                  lstPackageAtDesbranch.put(p.getPackageId(),currentBranchId);
              }
              else
                  continue;
          }
      }catch(Exception e)
      {
              System.out.println("Error : "+e.getMessage());
          return null;
      }
    if(!lstPackageAtDesbranch.isEmpty())
        return lstPackageAtDesbranch;
    return null;
  }
  /**
   * After modify (Unload or load package to trip)
   * call this method to change associate information and update information to database
   * @param tripId
   * @param branchId
   * @return
   *    true: save information successful
   *    false: error or dictPackageOfTrip have no item
   */
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public boolean savePackageTrip(int tripId,int branchId) {

        try{
        // Find Trip
        Trip trip=em.find(Trip.class, tripId);
        dictPackageOfTrip=getDictPackageOfTrip();
        if(dictPackageOfTrip.size()==0)
        {
           // System.out.println("Not have information about package in trip!!!");
            return false;
        }
       Enumeration<Integer> packageIds=dictPackageOfTrip.keys();
        while (packageIds.hasMoreElements()) {
            Integer packageId = packageIds.nextElement();
            int status=dictPackageOfTrip.get(packageId);
            // If add to trip - status=1
            aptech.eproject.logictics.db.Package p=em.find(aptech.eproject.logictics.db.Package.class, packageId);
            if(status==1)
            {
                // Update status delivery of package to consigning
                p.setDeliveryStatus(DeliveryStatus.Consigning);
                //change status of warehouse store package removed to IsRemove=True
                 if(em.find(Warehouse.class, new WarehousePK(branchId, packageId))!=null)
                {
                   Warehouse warehouse=em.find(Warehouse.class, new WarehousePK(branchId, packageId));
                   warehouse.setIsRemoved(true);
                   em.merge(warehouse);
                }
                //insert to trip change status IsUnload=false
                 TripPackage tripPackage=new TripPackage(new TripPackagePK(packageId, tripId),false);
                 tripPackage.setTrip(trip);
                tripPackage.setPackage(p);
                trip.addTripPackage(tripPackage);
            }
            // remove from trip add to warehouse
            if(status==2)
            {
                if(em.find(Warehouse.class, new WarehousePK(branchId, packageId))!=null)
                {
                   Warehouse warehouse=em.find(Warehouse.class, new WarehousePK(branchId, packageId));
                   warehouse.setIsRemoved(false);
                   em.merge(warehouse);
                }
                else
                {
                    //if package come to destination branch: update status delivery of package : delivering
                    if(isPackageAtDesBranch(packageId, branchId))
                        p.setDeliveryStatus(DeliveryStatus.Delivering);
                    else
                        p.setDeliveryStatus(DeliveryStatus.Waiting);
                    //Insert package to warehouse
                    Warehouse warehouse=new Warehouse(new WarehousePK(branchId, packageId), false);
                    em.persist(warehouse);
                }
                 // Change trip package status isUnload =true
                    TripPackage tripPackage=em.find(TripPackage.class, new TripPackagePK(packageId, tripId));
                    tripPackage.setIsUnloaded(true);
                    em.merge(tripPackage);
            }
            // update information about package to database
            em.merge(p);
        }
       // Update trip information
       em.merge(trip);
       return true;
    }
       catch(NullPointerException npe)
        {
             System.err.println("Not have enough information to process update trip package operation!!!");
             npe.printStackTrace();
             context.setRollbackOnly();
            return  false;
        }
          catch(EntityExistsException eee)
        {
            System.err.println("Entity have already existed");
            context.setRollbackOnly();
            return false;
        }
        catch(Exception e)
        {
            System.err.println("Error In update trip package information : "+e.getMessage());
            e.printStackTrace();
            context.setRollbackOnly();
            return false;
        }
    }
////////////////////////////////////Trip Route Service////////////////////////////////////
    /**
     * Call this method. When click Save button to change status of route in trip
     * change status of specified route in trip
     * @param routeId
     * @param status
     * @return
     *  next Route Id of this trip based on route order
     *  routeId same with param: The route is the last route in trip
     *  -1 : error
     */
    public Integer changeStatusTripRoute(int tripId,int routeId,TripRouteStatus status)
    {
        try
        {
          //  System.out.println("Trip ID : "+tripId+"Route ID : "+routeId+"Status : "+status);
            TripRoute routeInTrip=em.find(TripRoute.class,new TripRoutePK(routeId, tripId));
            switch(routeInTrip.getStatus())
            {
                case Arrived:
                    
                    break;
                case Coming:
                    if(status.equals(TripRouteStatus.Arrived))
                    {
                        routeInTrip.setStatus(status);
                        routeInTrip.setArriveDate(new Timestamp(new Date().getTime()));
                    }
                        break;
                case Planned:
                    routeInTrip.setStatus(status);
                    break;
                default:break;
            }
            em.merge(routeInTrip);
            // Set Trip Finished If All route In Trip is Finished
            boolean isTripFinsished=true;
            Trip trip=em.find(Trip.class,tripId);
            Iterator it=trip.getTripRoutes().iterator();
            while (it.hasNext()) {
                TripRoute tripRoute = (TripRoute)it.next();
                if(tripRoute.getArriveDate()!=null)
                {
                    continue;
                }
                else
                {
                    isTripFinsished=false;
                }
            }
            if(isTripFinsished)
            {
                trip.setArriveDate(new Timestamp(new Date().getTime()));
                trip.setIsFinished(true);
                em.merge(trip);
                Vehicle v=trip.getVehicle();
                v.setStatus(VehicleStatus.Free);
                em.merge(v);
            }
            // Get Next Route ID
            short routeOrder=(short) (routeInTrip.getRouteOrder() + 1);
            Query q=em.createQuery("SELECT tr.route.routeId FROM TripRoute tr WHERE tr.isDeleted = 'False'" +
                    " AND tr.trip.tripId=?1 AND tr.routeOrder=?2");
            q.setParameter(1, tripId);
          //  System.out.println(routeOrder);
            q.setParameter(2, routeOrder);
            return (Integer) q.getSingleResult();
        }
        catch(NoResultException nre)
        {
            System.out.println("This route id is the last route in trip");
            return routeId;
        }
        catch(NullPointerException npe)
        {
            System.err.println("cannot find route !!!!");
            return -1;
        }
        catch(Exception e)
        {
            System.err.println("Unknown error");
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * call when change status Planned to comming to fill what route in trip have status comming
     * @param tripId
     * @return
     *  the route of this trip have status comming
     *  null : There are any route in trip have status comming or error
     */
    public Route getRouteHaveStatusComming(int tripId)
    {
        Route route=null;
        Trip t=em.find(Trip.class, tripId);
        Iterator<TripRoute> tripRoutes=t.getTripRoutes().iterator();
        while (tripRoutes.hasNext()) {
            TripRoute tripRoute = tripRoutes.next();
            if(tripRoute.getStatus().equals(TripRouteStatus.Coming))
            {
                route=tripRoute.getRoute();
            }
        }
        return route;
    }

    /**
     * Get status of route in trip
     * @param routeId
     * @param tripId
     * @return
     *      current status of route in trip
     */
    public TripRouteStatus getTripRouteStatus(int routeId,int tripId)
    {
       TripRoute tripRoute=em.find(TripRoute.class, new TripRoutePK(routeId,tripId));
       return tripRoute.getStatus();
    }

    /**
     * Check to see whether lstRouteId(element type integer) contain route have status comming or not
     * @param lstRouteId
     * @param tripId
     * @return
     *      false call removeRouteInTrip()
     *      true show message 'Please choice only these route have status is planned or arrived'
     */
    public boolean isContainRouteComming(List lstRouteId,int tripId)
    {
        Route r=getRouteHaveStatusComming(tripId);
        if(lstRouteId.contains(r.getRouteId()))
        {
            return true;
        }
        else{
            return false;
        }
    }

    /**
     * Call when check to each route in List of route planned or arrived and click Delete
     * Remove list of route .
     * @param lstRouteDeleted
     * @return
     *  true: Delete successful
     *  false:  error in delete process
     */
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public boolean removeRouteInTrip(List lstRouteDeleted,int tripId)
    {

        try {

            for (int i = 0; i < lstRouteDeleted.size(); i++) {
                Integer routeId = (Integer) lstRouteDeleted.get(i);
                //change status Is deleted of these route is true
                TripRoute tr=em.find(TripRoute.class, new TripRoutePK(routeId,tripId));
                tr.setIsDeleted(true);
                em.merge(tr);
                Short routeOrder=tr.getRouteOrder();
                //update route order of other route
                Query q=em.createQuery("SELECT tr FROM TripRoute tr WHERE tr.isDeleted = 'False'" +
                    " AND tr.trip.tripId=?1 AND tr.routeOrder>?2 ORDER BY tr.routeOrder ASC");
                q.setParameter(1, tripId);
                q.setParameter(2, routeOrder);
               List otherRoutes=q.getResultList();
                for (int j = 0; j < otherRoutes.size(); j++) {
                   TripRoute otherTripRoute = (TripRoute) otherRoutes.get(j);
                    otherTripRoute.setRouteOrder((short) (otherTripRoute.getRouteOrder() - 1));
                    em.merge(otherTripRoute);
                }
            }
           //update maxRoute In trip
            Trip t=em.find(Trip.class, tripId);
            t.setMaxRoute((short) (t.getMaxRoute() - lstRouteDeleted.size()));
            em.merge(t);
            return true;
        }
        catch(ClassCastException cce)
        {
            System.err.println("Error in casting type..");
            return false;
        }
        catch (Exception e) {
            e.printStackTrace();
            context.setRollbackOnly();
            return false;
        }
    }

    public void saveVehicleOfTrip(int tripId)
    {
        try{
           Trip t=em.find(Trip.class, tripId);
           vehicleId=t.getVehicle().getVehicleId();
          // System.out.println("Saving Vehicle : "+vehicleId);
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Use to test is package already in destination branch or not
     * @param branchId
     * @param packageId
     * @return
     *  true :  package already in destination branch
     *  false: 
     */
    public boolean isPackageAlreadyInDesBranch(int branchId,int packageId)
    {
        if(isPackageAtDesBranch(packageId, branchId))
        {
            if(em.find(Warehouse.class, new WarehousePK(branchId, packageId))!=null)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    /**
     * Return Trip based on tripId
     * @param tripId
     * @return
     *  null If Cannot Trip not existed
     */
    public Trip findTrip(int tripId)
    {
        try {
            return em.find(Trip.class, tripId);
        } catch (Exception e) {
            System.out.println("Cannot find Trip!!!");
            return null;
        }
        
    }

   
}
